#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include "tagfs.h"

// return output to tagid, also return the number of existing tags since some input tags might not exist
int get_tagid_list(char** tag, unsigned int* tagid, int num_tag) 
{
	FILE* fp;
	struct tagid_name* entries;
	char system_filepath[256];
	unsigned int num, free_index;
	int i, j;
	int exist_num_tag = 0;
	get_system_filepath("/.tagid_tagname", system_filepath);
	fp = fopen(system_filepath, "rb");
	if(fp == NULL)
		return 0; // there is no tag at all
	// read the whole file to array
	// get the number of entries
	fseek(fp, 0, SEEK_END);
	num = ftell(fp)/sizeof(struct tagid_name);
	entries = (struct tagid_name*)malloc(sizeof(struct tagid_name) * num); // allocate for the whole map
	fseek(fp, 0, SEEK_SET);
	fread(entries, sizeof(struct tagid_name), num, fp);
	for(i=0; i<num_tag; i++)
	{
		for(j=0; j<num; j++)
		{
			if(!strcmp(entries[j].name, tag[i]))
			{
				tagid[exist_num_tag] = entries[j].id;
				exist_num_tag++;
				break;
			}
		}
	}
	fclose(fp);;
	free(entries);
	return exist_num_tag;
}


// remove inode from .inode_name
void remove_inode(unsigned int inode)
{
	FILE* fp;
	struct inode_name* entries;
	char system_filepath[256];
	unsigned int num, free_index;
	int i, j;
	get_system_filepath("/.inode_name", system_filepath);
	fp = fopen(system_filepath, "rb");
	if(fp == NULL)
		fp = fopen(system_filepath, "wb+");
	// read the whole file to array, delete the entry ,and write back to the file
	// get the number of entries
	fseek(fp, 0, SEEK_END);
	num = ftell(fp)/sizeof(struct inode_name);
	entries = (struct inode_name*)malloc(sizeof(struct inode_name) * num); // allocate for the whole map
	fseek(fp, 0, SEEK_SET);
	fread(entries, sizeof(struct inode_name), num, fp);
	for(i=0; i<num; i++)
	{
		// find entry
		if(entries[i].inode == inode)
		{
			memmove(&entries[i], &entries[i+1], (num - i - 1) * sizeof(struct inode_name));
			fclose(fp);
			// write entries back to the file
			fp = fopen(system_filepath, "wb");
			fwrite(entries, sizeof(struct inode_name), num-1, fp);
			fclose(fp);
			free(entries);
			return;
		}
	}
	fclose(fp);
	free(entries);
}

// remove tagid from .inode_tagid. If no tagid left then remove inode from .inode_name
void remove_tagid_from_inode(unsigned int inode, unsigned int tagid)
{
	FILE* fp;
	unsigned int *entries; // entries include size, inode number, tagid
	char system_filepath[256];
	unsigned int num;
	int i, j, size;
	int start_index, length;
	get_system_filepath("/.inode_tagid", system_filepath);
	fp = fopen(system_filepath, "rb");
	if(fp == NULL)
		fp = fopen(system_filepath, "wb+");
	// find the number of entries
	fseek(fp, 0, SEEK_END);
	num = ftell(fp)/sizeof(unsigned int);
	entries = (unsigned int*)malloc(sizeof(unsigned int) * num);
	fseek(fp, 0, SEEK_SET);
	fread(entries, sizeof(unsigned int), num, fp);
	// find start index to remove & the number of entries to remove (length)
	i = 0;
	while(i < num)
	{	
		if(entries[i+1] == inode)
		{
			size = entries[i];
			// j = 1 -> skip the first one which is an inode
			for(j=1; j < size-1; j++)
			{
				if(entries[i+j+1] == tagid)
				{
					// check if it is the only tag for the inode
					if(size == 3)
					{
						// need to remove the whole entry and also an inode from .inode_name
						start_index = i;
						length = 3;
						remove_inode(inode);
					}
					else
					{
						// decrese the size
						entries[i]--;
						// delete the entry
						start_index = i+j+1;
						length = 1;
					}
					memmove(&entries[start_index], &entries[start_index+length], (num - start_index - length) * sizeof(unsigned int));
					fclose(fp);
					// write entries back to the file
					fp = fopen(system_filepath, "wb");
					fwrite(entries, sizeof(unsigned int), num-length, fp);
					fclose(fp);
					free(entries);
					return;
				}
			}
			break;
		}
		else if(entries[i+1] > inode) // means there is no inode in the entries since the entries is sorted by inode
		{
			// remove nothing
			fclose(fp);
			free(entries);
			return;
		}
		else
		{
			i += entries[i]; // skip to the next entry by adding the size of current entry
		}
	}

	// exit from loop so there is no inode
	fclose(fp);
	free(entries);
}

// remove tagid from .tagid_tagname
void remove_tagid(unsigned int tagid)
{
	FILE* fp;
	struct tagid_name* entries;
	char system_filepath[256];
	unsigned int num, free_index;
	int i, j;
	get_system_filepath("/.tagid_tagname", system_filepath);
	fp = fopen(system_filepath, "rb");
	if(fp == NULL)
		fp = fopen(system_filepath, "wb+");
	// read the whole file to array, delete the entry ,and write back to the file
	// get the number of entries
	fseek(fp, 0, SEEK_END);
	num = ftell(fp)/sizeof(struct tagid_name);
	entries = (struct tagid_name*)malloc(sizeof(struct tagid_name) * num); // allocate for the whole map
	fseek(fp, 0, SEEK_SET);
	fread(entries, sizeof(struct tagid_name), num, fp);
	for(i=0; i<num; i++)
	{
		// find entry
		if(entries[i].id == tagid)
		{
			memmove(&entries[i], &entries[i+1], (num - i - 1) * sizeof(struct tagid_name));
			fclose(fp);
			// write entries back to the file
			fp = fopen(system_filepath, "wb");
			fwrite(entries, sizeof(struct tagid_name), num-1, fp);
			fclose(fp);
			free(entries);
			return;
		}
	}
	fclose(fp);
	free(entries);
}

// remove inode from .tagid_inode. If no inode left then remove tagid from .tagid_tagname
void remove_inode_from_tagid(unsigned int tagid, unsigned int inode)
{
	FILE* fp;
	unsigned int *entries; // entries include size, inode number, tagid
	char system_filepath[256];
	unsigned int num;
	int i, j, size;
	int start_index, length;
	get_system_filepath("/.tagid_inode", system_filepath);
	fp = fopen(system_filepath, "rb");
	if(fp == NULL)
		fp = fopen(system_filepath, "wb+");
	// find the number of entries
	fseek(fp, 0, SEEK_END);
	num = ftell(fp)/sizeof(unsigned int);
	entries = (unsigned int*)malloc(sizeof(unsigned int) * num);
	fseek(fp, 0, SEEK_SET);
	fread(entries, sizeof(unsigned int), num, fp);
	// find start index to remove & the number of entries to remove (length)
	i = 0;
	while(i < num)
	{	
		if(entries[i+1] == tagid)
		{
			size = entries[i];
			// j = 1 -> skip the first one which is a tagid
			for(j=1; j < size-1; j++)
			{
				if(entries[i+j+1] == inode)
				{
					// check if it is the only inode for the tag
					if(size == 3)
					{
						// need to remove the whole entry and also a tagid from .tagid_tagname
						start_index = i;
						length = 3;
						remove_tagid(tagid);
					}
					else
					{
						// decrese the size
						entries[i]--;
						// delete the entry
						start_index = i+j+1;
						length = 1;
					}
					memmove(&entries[start_index], &entries[start_index+length], (num - start_index - length) * sizeof(unsigned int));
					fclose(fp);
					// write entries back to the file
					fp = fopen(system_filepath, "wb");
					fwrite(entries, sizeof(unsigned int), num-length, fp);
					fclose(fp);
					free(entries);
					return;
				}
			}
			break;
		}
		else if(entries[i+1] > tagid) // means there is no tagid in the entries since the entries is sorted by tagid
		{
			// remove nothing
			fclose(fp);
			free(entries);
			return;
		}
		else
		{
			i += entries[i]; // skip to the next entry by adding the size of current entry
		}
	}

	// exit from loop so there is no inode
	fclose(fp);
	free(entries);
}


// remove tagid from .inode_tagid
	// if no tagid left remove inode from .inode_name
// remove inode from .tagid_inode
	// if no inode left remove tagid from .tagid_tagname
int main(int argc, char *argv[])
{
	FILE* fp;
	int i;
	int num_tag;
	int res;
	char* filename;
	char fullpath[256];
	char** tag;
	unsigned int* tagid;
	
	num_tag = argc-2;
	filename = argv[1];
	// get the absolute path for the file as it is needed to find its inode number
	getcwd(fullpath, 256);
	strcat(fullpath, "/");
	strcat(fullpath, filename);
	clean_path(fullpath); // clean . and ..

	tag = (char**)malloc(num_tag*sizeof(char*));
	for(i=2; i<argc; i++)
	{
		tag[i-2] = argv[i];
	}
	
	tagid = (unsigned int*)malloc(num_tag*sizeof(unsigned int));
	
	// get file inode
	struct stat *statbuf = (struct stat*)malloc(sizeof(struct stat));
	res = lstat(fullpath, statbuf);
	if(res)
	{
		printf("file does not exist!!!\n");
		return 1;
	}
	// get the list of tagid from tag name from .tagid_tagname file
	num_tag = get_tagid_list(tag, tagid, num_tag);
	// remove tagid from .inode_tagid
	for(i=0; i<num_tag; i++)
	{
		remove_tagid_from_inode(statbuf->st_ino, tagid[i]);
	}
	// remove inode from .tagid_inode
	for(i=0; i<num_tag; i++)
	{
		remove_inode_from_tagid(tagid[i], statbuf->st_ino);
	}	

	//print_tagid_tagname();
	//print_inode_name();
	//print_tagid_inode();
	//print_inode_tagid();
	
	return 1;
}
